#include "string_util.h"
#include <string.h>
#include <algorithm>

namespace base
{
namespace utils
{
StringUtil::StringUtil() = default;

std::string StringUtil::toLower(const std::string &origin_str)
{
  std::string result = origin_str;
  std::transform(result.begin(), result.end(), result.begin(), ::tolower);
  return result;
}
std::string StringUtil::toUpper(const std::string &origin_str)
{
  std::string result = origin_str;
  std::transform(result.begin(), result.end(), result.begin(), ::toupper);
  return result;
}
bool StringUtil::startsWith(const std::string &origin_str, const std::string &substr, CaseSensitivity cs)
{
  if (CaseSensitivity::CaseSensitive == cs)
  {
    return origin_str.find(substr) == 0;
  }
  return toLower(origin_str).find(toLower(substr)) == 0;
}
bool StringUtil::endsWith(const std::string &origin_str, const std::string &substr, CaseSensitivity cs)
{
  std::size_t last_index = origin_str.length() - substr.length();
  if (CaseSensitivity::CaseSensitive == cs)
  {
    return origin_str.rfind(substr) == last_index;
  }
  return toLower(origin_str).rfind(toLower(substr)) == last_index;
}
int StringUtil::compare(const std::string &s1, const std::string &s2, CaseSensitivity cs)
{
  if (CaseSensitivity::CaseSensitive == cs)
  {
    return strcmp(s1.c_str(), s2.c_str());
  }
  return strcmp(toLower(s1).c_str(), toLower(s2).c_str());
}
bool StringUtil::contains(const std::string &str, const std::string &substr, CaseSensitivity cs)
{
  if (CaseSensitivity::CaseSensitive == cs)
  {
    return str.find(substr) != std::string::npos;
  }
  return toLower(substr).find(toLower(substr)) != std::string::npos;
}
std::vector<std::string> StringUtil::split(const std::string &str, const std::string &sep, SplitBehavior behavior, CaseSensitivity cs)
{
  std::string check_str = CaseSensitivity::CaseSensitive == cs ? str : toLower(str);
  std::string check_sep = CaseSensitivity::CaseSensitive == cs ? sep : toLower(sep);

  size_t length = check_sep.length();
  std::size_t index = check_str.find(check_sep);
  std::size_t start_index = 0;
  std::string element;

  std::vector<std::string> result;
  while (true)
  {
    element = str.substr(start_index, index - start_index);
    if (!element.empty() || (element.empty() && SplitBehavior::KeepEmptyParts == behavior))
    {
      result.emplace_back(element);
    }
    if (std::string::npos == index)
    {
      break;
    }
    start_index = index + length;
    index = check_str.find(check_sep, start_index);
  }
  return result;
}
std::string StringUtil::trimLeft(const std::string &str, const std::string &token)
{
  std::string result = str;
  result.erase(0, result.find_first_not_of(token));
  return result;
}
std::string StringUtil::trimRight(const std::string &str, const std::string &token)
{
  std::string result = str;
  result.erase(result.find_last_not_of(token) + 1);
  return result;
}
std::string StringUtil::trim(const std::string &str, const std::string &token)
{
  std::string result = str;
  result.erase(0, result.find_first_not_of(token));
  result.erase(result.find_last_not_of(token) + 1);
  return result;
}
std::string StringUtil::trimmed(const std::string &str)
{
  std::string result = str;
  std::string token = "\t\n\v\f\r ";
  result.erase(0, result.find_first_not_of(token));
  result.erase(result.find_last_not_of(token) + 1);
  return result;
}
std::string StringUtil::replace(const std::string &str, const std::string &src, const std::string &dest)
{
  std::string result;
  std::string::size_type pos_begin = 0;
  std::string::size_type pos = str.find(src);
  while (std::string::npos != pos)
  {
    result.append(str, pos_begin, pos - pos_begin);
    result.append(dest);
    pos_begin = pos + src.length();
    pos = str.find(src, pos_begin);
  }
  if (pos_begin < str.length())
  {
    result.append(str.begin() + pos_begin, str.end());
  }

  return result;
}

}
}
